import json
import time
from paho.mqtt import client as mqtt_client
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QScrollArea, \
    QPushButton, QFrame
from PyQt5.QtGui import QFont
from PyQt5.QtCore import QThread, pyqtSignal, Qt
import sys
import matplotlib.pyplot as plt
import matplotlib

matplotlib.use('Qt5Agg')
matplotlib.rcParams['interactive'] = False
matplotlib.rcParams['font.sans-serif'] = ['SimSun']  # 使用 SimSun 字体，支持中文
matplotlib.rcParams['font.family'] = 'sans-serif'
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import numpy as np
import datetime

# MQTT broker相关配置
broker = '81f4cbc88e.st1.iotda-device.cn-east-3.myhuaweicloud.com'
port = 1883
client_id = '6730a9fb30d2f15dece6106b_region-01_0_0_2025042209'
username = '6730a9fb30d2f15dece6106b_region-01'
password = '6583c285a6443fff4ca25743a1f03133fdbe19d3a797060a532225d85d4554f7'

# MQTT上报主题
publish_topic_template = "$oc/devices/6730a9fb30d2f15dece6106b_region-01/sys/properties/report"


# 连接MQTT broker回调函数
def on_connect(client, userdata, flags, rc, props):
    if rc == 0:
        print("Connected to MQTT Broker!")
        client.subscribe("$oc/devices/6730a9fb30d2f15dece6106b_region-01/sys/shadow/get/response")
    else:
        print(f"Failed to connect, return code {rc}\n")


# 接收到消息的回调函数，处理响应消息及传感器数据更新、界面显示
def on_message(client, userdata, msg, display_widget):
    try:
        payload = msg.payload.decode('utf-8')
        data_dict = json.loads(payload)
        reported_data = data_dict.get('shadow', [{}])[0].get('reported', {})

        device_status = reported_data.get('properties', {}).get('DeviceStatus')
        device_id = reported_data.get('properties', {}).get('DeviceID')
        air_temperature = reported_data.get('properties', {}).get('Airtemperature')
        air_humidity = reported_data.get('properties', {}).get('Airhumidity')
        soil_moisture = reported_data.get('properties', {}).get('Soilmoisture')
        light_intensity = reported_data.get('properties', {}).get('Lightintensity')
        water_level = reported_data.get('properties', {}).get('waterlevel')

        # 修改时间戳的生成，只保留月、日、时分秒
        timestamp = datetime.datetime.now().strftime('%m-%d %H:%M:%S')

        if display_widget:
            # 使用信号量来更新UI，确保线程安全
            if device_status is not None:
                display_widget.device_status_signal.emit(f"设备状态: {device_status}")
            if device_id is not None:
                display_widget.device_id_signal.emit(f"设备ID: {device_id}")

            # 更新传感器数据
            if air_temperature is not None:
                display_widget.update_line_charts([('气温', str(air_temperature), timestamp)])
                if air_humidity is not None:
                    display_widget.update_line_charts([('空气湿度', str(air_humidity), timestamp)])
                if soil_moisture is not None:
                    display_widget.update_line_charts([('土壤湿度', str(soil_moisture), timestamp)])
                if light_intensity is not None:
                    display_widget.update_line_charts([('光照强度', str(light_intensity), timestamp)])
                if water_level is not None:
                    display_widget.update_water_rectangle('水位', str(water_level), timestamp)

    except json.JSONDecodeError as e:
        print(f"解析消息JSON数据出错: {e}")
    except Exception as ex:
        print(f"处理消息时出现其他未知异常: {ex}")


def connect_mqtt():
    client = mqtt_client.Client(mqtt_client.CallbackAPIVersion.VERSION2, client_id)
    client.username_pw_set(username, password)
    client.connect(broker, port)
    return client


class MQTTWorker(QThread):
    data_received = pyqtSignal(dict)

    def __init__(self, display_widget):
        super().__init__()
        self.display_widget = display_widget
        self.client = None
        self.running = True

    def run(self):
        try:
            self.client = connect_mqtt()
            self.client.on_connect = on_connect
            self.client.on_message = lambda client, userdata, msg: on_message(client, userdata, msg,
                                                                              self.display_widget)
            self.client.loop_start()

            # 持续请求数据
            while self.running:
                self.request_data()
                time.sleep(10)

        except Exception as e:
            print(f"MQTTWorker线程出现异常: {e}")
        finally:
            if self.client:
                self.client.loop_stop()
                self.client.disconnect()

    def request_data(self):
        request_id = str(int(time.time()))
        publish_topic = "$oc/devices/6730a9fb30d2f15dece6106b_region-01/sys/shadow/get/request_id={}".format(request_id)

        msg = {
            "object_device_id": "6730a9fb30d2f15dece6106b_region-01",
            "service_id": "Detectiondata"
        }
        msg_json = json.dumps(msg)
        result = self.client.publish(publish_topic, msg_json)
        status = result[0]
        if status == 0:
            print(f"Send `{msg_json}` to topic `{publish_topic}`")
        else:
            print(f"Failed to send message to topic {publish_topic}")

    def send_command(self, device_type, value):
        if not self.client:
            return

        payload = {
            "services": [{
                "service_id": "ExpectedValue",
                "properties": {
                    device_type: value
                }
            }]
        }

        self.client.publish(
            publish_topic_template,
            json.dumps(payload),
            qos=1
        )
        print(f"已发送：{device_type}={value}")

    def stop(self):
        self.running = False
        self.wait()


class DataDisplayWidget(QWidget):
    device_status_signal = pyqtSignal(str)
    device_id_signal = pyqtSignal(str)
    fan_status_signal = pyqtSignal(str)
    pump_status_signal = pyqtSignal(str)
    heat_status_signal = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.init_ui()
        self.connect_signals()
        self.data_history = {
            '气温': [],
            '空气湿度': [],
            '土壤湿度': [],
            '光照强度': [],
            '水位': []
        }
        self.current_sensor = '气温'  # 初始化当前展示的传感器类型
        self.mqtt_worker = None

    def init_ui(self):
        self.setStyleSheet("""
            QWidget {
                background-color: #282C34;
                color: #ABB2BF;
            }
            QLabel {
                font-size: 14px;
            }
            QPushButton {
                background-color: #4B535D;
                border: 1px solid #4B535D;
                border-radius: 5px;
                color: #ABB2BF;
                padding: 5px;
                min-width: 100px;
                min-height: 30px;
            }
            QPushButton:hover {
                background-color: #5C6370;
                border: 1px solid #5C6370;
            }
            QPushButton:pressed {
                background-color: #3E444C;
                border: 1px solid #3E444C;
            }
            QScrollArea {
                border: none;
            }
            QFrame {
                border: 1px solid #4B535D;
                border-radius: 5px;
                margin: 5px;
            }
        """)  # 使用QSS样式表来设置样式

        main_layout = QHBoxLayout(self)

        left_layout = QVBoxLayout()

        # 先添加设备状态和设备ID的标签
        top_info_layout = QHBoxLayout()
        self.device_status_label = QLabel("设备状态: 无数据")
        self.device_status_label.setFont(QFont("SimSun", 12))  # 使用SimSun字体
        self.device_id_label = QLabel("设备ID: 无数据")
        self.device_id_label.setFont(QFont("SimSun", 12))  # 使用SimSun字体
        top_info_layout.addWidget(self.device_status_label)
        top_info_layout.addWidget(self.device_id_label)
        left_layout.addLayout(top_info_layout)

        scroll_area = QScrollArea()
        scroll_area.setWidgetResizable(True)

        self.figure = plt.figure(figsize=(6, 4))
        self.canvas = FigureCanvas(self.figure)
        scroll_area.setWidget(self.canvas)
        left_layout.addWidget(scroll_area)

        # 添加设备状态、设备ID、风扇状态和水泵状态的标签
        bottom_info_layout = QHBoxLayout()
        self.fan_status_label = QLabel("风扇状态: 无数据")
        self.fan_status_label.setFont(QFont("SimSun", 12))  # 使用SimSun字体
        self.pump_status_label = QLabel("水泵状态: 无数据")
        self.pump_status_label.setFont(QFont("SimSun", 12))  # 使用SimSun字体
        self.heat_status_label = QLabel("加热状态: 无数据")
        self.heat_status_label.setFont(QFont("SimSun", 12))  # 使用SimSun字体
        bottom_info_layout.addWidget(self.fan_status_label)
        bottom_info_layout.addWidget(self.pump_status_label)
        bottom_info_layout.addWidget(self.heat_status_label)
        left_layout.addLayout(bottom_info_layout)

        right_layout = QVBoxLayout()

        button_layout = QVBoxLayout()
        self.btn_temperature = QPushButton("气温折线图")
        self.btn_humidity = QPushButton("空气湿度折线图")
        self.btn_soil_moisture = QPushButton("土壤湿度折线图")
        self.btn_light_intensity = QPushButton("光照强度折线图")
        self.btn_water_level = QPushButton("水位矩形图")
        self.btn_fan_forward = QPushButton("风扇正转")
        self.btn_fan_reverse = QPushButton("风扇反转")
        self.btn_fan_off = QPushButton("风扇关")
        self.btn_pump_on = QPushButton("水泵开")
        self.btn_pump_off = QPushButton("水泵关")
        self.btn_heat_on = QPushButton("加热开")
        self.btn_heat_off = QPushButton("加热关")

        button_layout.addWidget(self.btn_temperature)
        button_layout.addWidget(self.btn_humidity)
        button_layout.addWidget(self.btn_soil_moisture)
        button_layout.addWidget(self.btn_light_intensity)
        button_layout.addWidget(self.btn_water_level)
        button_layout.addWidget(self.btn_fan_forward)
        button_layout.addWidget(self.btn_fan_reverse)
        button_layout.addWidget(self.btn_fan_off)
        button_layout.addWidget(self.btn_pump_on)
        button_layout.addWidget(self.btn_pump_off)
        button_layout.addWidget(self.btn_heat_on)
        button_layout.addWidget(self.btn_heat_off)

        # 使用QFrame对按钮进行分组
        button_frame = QFrame()
        button_frame.setLayout(button_layout)
        right_layout.addWidget(button_frame)

        main_layout.addLayout(left_layout)
        main_layout.addLayout(right_layout)

        self.setLayout(main_layout)

    def connect_signals(self):
        self.btn_temperature.clicked.connect(lambda: self.switch_chart('气温'))
        self.btn_humidity.clicked.connect(lambda: self.switch_chart('空气湿度'))
        self.btn_soil_moisture.clicked.connect(lambda: self.switch_chart('土壤湿度'))
        self.btn_light_intensity.clicked.connect(lambda: self.switch_chart('光照强度'))
        self.btn_water_level.clicked.connect(lambda: self.switch_chart('水位'))

        self.device_status_signal.connect(self.device_status_label.setText)
        self.device_id_signal.connect(self.device_id_label.setText)
        self.fan_status_signal.connect(self.fan_status_label.setText)
        self.pump_status_signal.connect(self.pump_status_label.setText)
        self.heat_status_signal.connect(self.heat_status_label.setText)

        # 设备控制按钮点击事件连接
        self.btn_fan_forward.clicked.connect(self.send_fan_forward)
        self.btn_fan_reverse.clicked.connect(self.send_fan_reverse)
        self.btn_fan_off.clicked.connect(self.send_fan_off)
        self.btn_pump_on.clicked.connect(self.send_pump_on)
        self.btn_pump_off.clicked.connect(self.send_pump_off)
        self.btn_heat_on.clicked.connect(self.send_heat_on)
        self.btn_heat_off.clicked.connect(self.send_heat_off)

    def send_fan_forward(self):
        if self.mqtt_worker:
            self.mqtt_worker.send_command("FengSan", 1)
            self.fan_status_signal.emit("风扇状态: 正转")

    def send_fan_reverse(self):
        if self.mqtt_worker:
            self.mqtt_worker.send_command("FengSan", 2)
            self.fan_status_signal.emit("风扇状态: 反转")

    def send_fan_off(self):
        if self.mqtt_worker:
            self.mqtt_worker.send_command("FengSan", 0)
            self.fan_status_signal.emit("风扇状态: 关")

    def send_pump_on(self):
        if self.mqtt_worker:
            self.mqtt_worker.send_command("ShuiBeng", 1)
            self.pump_status_signal.emit("水泵状态: 开")

    def send_pump_off(self):
        if self.mqtt_worker:
            self.mqtt_worker.send_command("ShuiBeng", 0)
            self.pump_status_signal.emit("水泵状态: 关")

    def send_heat_on(self):
        if self.mqtt_worker:
            self.mqtt_worker.send_command("JiaRe", 1)
            self.heat_status_signal.emit("加热状态: 开")

    def send_heat_off(self):
        if self.mqtt_worker:
            self.mqtt_worker.send_command("JiaRe", 0)
            self.heat_status_signal.emit("加热状态: 关")

    def update_line_charts(self, data):
        for name, value, timestamp in data:
            if value != "无数据":
                try:
                    float_value = float(value)
                    self.data_history[name].append((float_value, timestamp))
                    if len(self.data_history[name]) > 20:
                        self.data_history[name].pop(0)
                    if self.current_sensor == name:
                        self.update_chart()
                except ValueError:
                    print(f"接收到的 {name} 数据 {value} 无法转换为浮点数，已忽略该条数据")
                    continue

    def update_water_rectangle(self, name, value, timestamp):
        if value != "无数据":
            try:
                float_value = float(value)
                self.data_history[name].append((float_value, timestamp))
                if len(self.data_history[name]) > 20:
                    self.data_history[name].pop(0)
                if self.current_sensor == '水位':
                    self.update_water_rectangle_chart()
            except ValueError:
                print(f"接收到的 {name} 数据 {value} 无法转换为浮点数，已忽略该条数据")

    def update_water_rectangle_chart(self):
        ax = self.figure.gca() if self.figure.axes else self.figure.add_subplot(111)
        ax.clear()

        if self.data_history['水位']:
            value = self.data_history['水位'][-1][0]  # 获取最新的水位值
            value = min(max(value, 0), 100)  # 限制数值在0-100之间
            ax.bar(0, 100, width=0.5, color='white', align='center', edgecolor='black',
                   linewidth=1)  # 绘制高度为100的黑框
            ax.bar(0, value, width=0.5, color='blue', align='center', edgecolor='black', linewidth=1)  # 绘制填充颜色部分
            ax.set_ylim(0, 100)
            ax.set_title('水位数据图表')
            ax.text(0, value + 2, str(value), ha='center', va='bottom')
        ax.axis('off')
        self.canvas.draw()

    def update_chart(self):
        ax = self.figure.gca() if self.figure.axes else self.figure.add_subplot(111)
        ax.clear()

        if self.current_sensor == '水位':
            self.update_water_rectangle_chart()
        else:
            if self.data_history[self.current_sensor]:
                time_stamps = [entry[1] for entry in self.data_history[self.current_sensor]]
                values = [entry[0] for entry in self.data_history[self.current_sensor]]
                ax.plot(time_stamps, values, marker='o', label=self.current_sensor)
                ax.set_title(f'{self.current_sensor}数据图表')
                ax.set_xlabel('时间')
                ax.set_ylabel('数值')
                ax.legend()
                ax.set_xticks(range(len(time_stamps)))
                ax.set_xticklabels(time_stamps, rotation=45, ha='right')
                ax.xaxis.set_tick_params(rotation=45)

                # 添加数据标签
                for i in range(len(values)):
                    ax.annotate(str(values[i]), xy=(time_stamps[i], values[i]), xytext=(0, 5),
                                textcoords='offset points', ha='center')
        self.canvas.draw()

    def switch_chart(self, sensor_name):
        self.current_sensor = sensor_name
        self.update_chart()

    def closeEvent(self, event):
        if self.mqtt_worker:
            self.mqtt_worker.stop()
        event.accept()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    display_widget = DataDisplayWidget()
    display_widget.setFixedSize(1000, 800)
    display_widget.show()

    # 创建MQTT工作线程
    display_widget.mqtt_worker = MQTTWorker(display_widget)
    display_widget.mqtt_worker.start()

    sys.exit(app.exec_())